<?php

namespace app\common\service;

use app\common\model\OrderCommon;
use GuzzleHttp\Client;
use Psr\Http\Message\StreamInterface;
use think\facade\Cache;

class ByteService extends PlatformBaseService
{
    public $platform = PlatFormFactory::BYTE;
    private $app_id;
    private $app_secret;
    private $salt;
    private $token;
    private $client;

    /**
     *
     */
    public function __construct()
    {
        parent::__construct();

        $this->app_id = $this->config['appid'];
        $this->app_secret = $this->config['secret'];
        $this->salt = $this->config['salt'];
        $this->token = $this->config['token'];

        $this->client = new Client([
            'base_uri' => 'https://developer.toutiao.com',
            'timeout'  => 3.0,
        ]);
    }

    /**
     * @param $code
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException|\Throwable
     */
    public function parseCode($code = null)
    {
        if ($code) {
            Cache::delete('byte_code2Session');
        }

        return Cache::remember('byte_code2Session', function () use ($code) {
            return $this->post('/api/apps/v2/jscode2session', [
                'json' => [
                    'appid'  => $this->app_id,
                    'secret' => $this->app_secret,
                    'code'   => $code
                ]
            ]);
        });
    }

    /**
     * @param string $path
     * @param string $scene
     * @return \Psr\Http\Message\StreamInterface
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Throwable
     */
    public function qrcode(string $path, string $scene): StreamInterface
    {
        $resp = $this->client->post('/api/apps/qrcode', [
            'json' => [
                'access_token' => $this->getAccessToken(),
                'appname'      => 'douyin',
                'path'         => urlencode($path . '?' . $scene),
                'set_icon'     => true,
            ]
        ]);

        $content = json_decode($resp->getBody()->getContents(), true);
        if (isset($content['errcode']) && $content['errcode'] > 0) {
            abort(400, $content['errmsg']);
        }

        return $resp->getBody();
    }

    /**预下单
     * @param \app\common\model\OrderCommon $model
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException|\Throwable
     */
    public function createOrder(OrderCommon $model)
    {
        $totalFee = $model->amount_payable;
        $body = $model->getPaymentBody();

        $json = [
            'app_id'       => $this->app_id,
            'out_order_no' => $model->order_sn,
            'total_amount' => $totalFee,
            'subject'      => $body,
            'body'         => $body,
            'valid_time'   => 1800,
            'notify_url'   => url('PayNotify/byte')->suffix(false)->domain(true)->build(),
        ];

        $json['sign'] = $this->sign($json);

        $resp = $this->post('/api/apps/ecpay/v1/create_order', [
            'json' => $json
        ]);

//        if ($model instanceof Order) {
//            $this->pushOrder($model);
//        }

        return $resp;
    }

    /**退款
     * @param \app\common\model\OrderCommon $model
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function refundOrder(OrderCommon $model)
    {
        $json = [
            'app_id'        => $this->app_id,
            'out_order_no'  => $model->sn,
            'out_refund_no' => $model->out_refund_no,
            'reason'        => $model->refundDesc(),
            'refund_amount' => $model->getData('paid_amount'),
        ];

        $json['sign'] = $this->sign($json);

        $this->post('/api/apps/ecpay/v1/create_refund', [
            'json' => $json
        ]);
    }

    /**
     * @param \app\common\model\OrderCommon $orderCommon
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Throwable
     */
    public function pushOrder(OrderCommon $orderCommon)
    {
        /** @var \think\Collection $items */
        $items = $orderCommon->items;
        $itemList = $items->map(function (OrderItem $item) {
            return [
                'item_code' => (string)$item->lesson_id,
                'img'       => full_url($item->lesson_image),
                'title'     => $item->lesson_name,
                'price'     => $item->total_amount * 100,
            ];
        });


        $this->post('/api/apps/order/v2/push', [
            'json' => [
                'access_token' => $this->getAccessToken(),
                'app_name'     => 'douyin',
                'open_id'      => $orderCommon->user->thirdByte->openid,
                'order_status' => 0,
                'order_type'   => 0,
                'update_time'  => Carbon::now()->unix() * 1000,
                'order_detail' => json_encode([
                    'order_id'    => $orderCommon->sn,
                    'create_time' => Carbon::parse($orderCommon->createtime)->unix() * 1000,
                    'status'      => '待支付',
                    'amount'      => $orderCommon->quantity,
                    'total_price' => $orderCommon->amount_payable,
                    'detail_url'  => 'sub-pages/user-center/orders',
                    'item_list'   => $itemList->all(),
                ]),
            ]
        ]);
    }

    /**
     * @return false|string
     * @throws \GuzzleHttp\Exception\GuzzleException|\Throwable
     */
    public function getPhoneNumber()
    {
        validate([
            'iv'            => 'require',
            'encryptedData' => 'require'
        ])->check(request()->param());

        $encryptedData = base64_decode(request()->param('encryptedData'));

        $res = $this->parseCode();

        $sessionKey = base64_decode($res['session_key']);

        $iv = base64_decode(request()->param('iv'));

        $result = json_decode(openssl_decrypt($encryptedData, 'AES-128-CBC', $sessionKey, OPENSSL_RAW_DATA, $iv), true);

        if (!isset($result['purePhoneNumber'])) {
            abort(400, '获取手机号失败');
        }

        return $result['purePhoneNumber'];
    }

    /**支付验签
     * @param $map
     * @return bool
     */
    public function check_sign($map): bool
    {
        $rList = [
            'token'     => $this->token,
            'timestamp' => $map['timestamp'],
            'nonce'     => $map['nonce'],
            'msg'       => htmlspecialchars_decode(htmlspecialchars_decode($map['msg'])),
        ];

        sort($rList, 2);

        $sha1 = sha1(implode('', $rList));

        if ($sha1 === $map['msg_signature']) {
            return true;
        }

        return false;
    }

    /**支付签名
     * @param $map
     * @return string
     */
    private function sign($map): string
    {
        $rList = array();
        foreach ($map as $k => $v) {
            if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id") {
                continue;
            }
            $value = trim(strval($v));
            $len = strlen($value);
            if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"") {
                $value = substr($value, 1, $len - 1);
            }
            $value = trim($value);
            if ($value == "" || $value == "null") {
                continue;
            }
            $rList[] = $value;
        }
        $rList[] = $this->salt;
        sort($rList, 2);
        return md5(implode('&', $rList));
    }

    /**
     * @param $image
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Throwable
     */
    // public function checkImage($image): void
    // {
    //     $resp = $this->client->post('/api/apps/censor/image', [
    //         'json' => [
    //             'app_id'       => $this->app_id,
    //             'access_token' => $this->getAccessToken(),
    //             'image'        => $image,
    //         ]
    //     ]);

    //     $content = json_decode($resp->getBody()->getContents(), true);

    //     if (isset($content['error']) && $content['error'] > 0) {
    //         abort(400, $content['message']);
    //     }

    //     foreach ($content['predicts'] as $predict) {
    //         if ($predict['hit']) {
    //             abort(400, '图片违规，请重新上传:' . $predict['model_name']);
    //         }
    //     }
    // }

    /**
     * @return string
     * @throws \Throwable
     */
    private function getAccessToken(): string
    {
        return Cache::remember('access_token', function () {
            $data = $this->post('/api/apps/v2/token', [
                'json' => [
                    'appid'      => $this->app_id,
                    'secret'     => $this->app_secret,
                    'grant_type' => 'client_credential',
                ]
            ]);

            return $data['access_token'];
        }, 60 * 60 * 2);
    }


    /**
     * @param $url
     * @param array $json
     * @return mixed
     * @throws \Exception|\GuzzleHttp\Exception\GuzzleException
     */
    private function post($url, array $json)
    {
        $resp = $this->client->post($url, $json);

        $content = json_decode($resp->getBody()->getContents(), true);

        if (isset($content['err_no']) && $content['err_no'] > 0) {
            abort(400, $content['err_tips']);
        }

        if (isset($content['err_code']) && $content['err_code'] > 0) {
            abort(400, $content['err_msg']);
        }

        return $content['data'] ?? '';
    }
}